package com.olda.play.auth.security.config.security;

import cn.binarywang.wx.miniapp.api.WxMaService;
import com.olda.play.auth.security.filter.MyUsernamePasswordAuthenticationFilter;
import com.olda.play.auth.security.handler.MyUsernamePasswordAuthenticationFailureHandler;
import com.olda.play.auth.security.handler.MyUsernamePasswordAuthenticationSuccessHandler;
import com.olda.play.auth.security.provider.wechat.WechatAuthenticationProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY;
import static org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    private final UserDetailsService systemUserDetailsService;
    private final UserDetailsService wechatUserDetailsService;
//    private final LoginService loginService;
    private final WxMaService wxMaService;
    private final MyUsernamePasswordAuthenticationSuccessHandler myUsernamePasswordAuthenticationSuccessHandler;
    private final MyUsernamePasswordAuthenticationFailureHandler myUsernamePasswordAuthenticationFailureHandler;




    /**
     * 重写此方法配置-httpSecurity
     *  通过重载该方法，可配置如何通过拦截器保护请求。
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/oauth/**","/oauth/token").permitAll()
               // 由于使用的是JWT，我们这里不需要csrf
               .and().csrf().disable();
        //在 UsernamePasswordAuthenticationFilter 之前添加
        http .addFilterBefore(myUsernamePasswordAuthenticationFilter(),MyUsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(wechatAuthenticationProvider())
                .authenticationProvider(daoAuthenticationProvider())
        ;
    }


    /*@Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }*/

    /**
     * AuthenticationManager--认证相关的核心接口--同时也是发起认证的出发点，
     * @return
     * @throws Exception
     */
    @Override
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 微信认证授权提供者
     *
     * @return
     */
    @Bean
    public WechatAuthenticationProvider wechatAuthenticationProvider() {
        WechatAuthenticationProvider provider = new WechatAuthenticationProvider();
        provider.setUserDetailsService(wechatUserDetailsService);
        provider.setWxMaService(wxMaService);
//        provider.setLoginService(loginService);
        return provider;
    }

    /**
     * 用户名密码认证授权提供者
     *
     * @return
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(systemUserDetailsService);
        //todo 设置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        //todo  // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常，默认:true-隐藏；false-抛出异常；
        return provider;
    }



    /**
     *
     * @return
     * @throws Exception
     */
    @Bean
    public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter=new MyUsernamePasswordAuthenticationFilter();
        //设置仅支持post请求
        myUsernamePasswordAuthenticationFilter.setPostOnly(true);
        //必须指定 authenticationManager---- 密码模式下配置认证管理器 AuthenticationManager
        myUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        //设置确定是否需要身份验证的 URL
        myUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/oauth/getAuthToken1");
        //设置用于从登录请求中获取用户名的参数名称-- 默认是 username
        myUsernamePasswordAuthenticationFilter.setUsernameParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
        //设置用于从登录请求中获取密码的参数名称-- 默认是 password
        myUsernamePasswordAuthenticationFilter.setPasswordParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
        myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(myUsernamePasswordAuthenticationSuccessHandler);
        myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(myUsernamePasswordAuthenticationFailureHandler);
        return myUsernamePasswordAuthenticationFilter;
    }

    /**
     * 密码编码器
     * <p>
     * 委托方式，根据密码的前缀选择对应的encoder，例如：{bcypt}前缀->标识BCYPT算法加密；{noop}->标识不使用任何加密即明文的方式
     * 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
